package concurrency.chapter10;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

/**
 * Exception in thread "T1" java.lang.IllegalMonitorStateException
 * at java.base/java.lang.Object.notifyAll(Native Method)
 * Object.wait()和Object.notify()和Object.notifyall()这些方法只能在同步方法中调用，
 * 必须写在synchronized方法内部或者synchronized块内部。
 * 如果当前线程不是锁的持有者，该方法抛出一个IllegalMonitorStateException异常。
 * 这是因为：这几个方法要求当前正在运行object.wait()方法的线程拥有object的对象锁。
 * 即使你确实知道当前上下文线程确实拥有了对象锁，也不能将object.wait()这样的语句写在当前上下文中。
 */
public class BooleanLock implements Lock {
    //The initValue is true indicated the lock have be get.
    //The initValue is false indicated the lock is free(other thread can be get this.)
    private boolean initValue;
    private Collection<Thread> blockedThreadCollection = new ArrayList<>();
    private Thread currentThread;

    public BooleanLock() {
        this.initValue = false;
    }

    @Override
    public synchronized void lock() throws InterruptedException {
        while (initValue) {
            //锁已经被拿了，获取不到锁，则加入到集合
            blockedThreadCollection.add(Thread.currentThread());
            //monitor是BooleanLock的一个实例
            this.wait();
        }

        //得到锁，则从阻塞列表里删除
        blockedThreadCollection.remove(Thread.currentThread());
        //释放标识锁
        this.initValue = true;
        this.currentThread = Thread.currentThread();
    }

    @Override
    public synchronized void lock(long mills) throws InterruptedException, TimeOutException {
        if (mills <= 0)
            lock();

        long hasRemaining = mills;
        long endTime = System.currentTimeMillis() + mills;
        while (initValue) {
            if (hasRemaining <= 0)
                throw new TimeOutException("Time out");
            blockedThreadCollection.add(Thread.currentThread());
            this.wait(mills);
            hasRemaining = endTime - System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ">>" + hasRemaining);
        }
        this.initValue = true;
        this.currentThread = Thread.currentThread();
    }

    @Override
    public synchronized void unlock() {
        //只有谁加的锁，谁才可以释放
        if (Thread.currentThread() == currentThread) {
            this.initValue = false;
            this.notifyAll();
            Optional.of(Thread.currentThread().getName() + " release the lock monitor").ifPresent(System.out::println);
        }
    }

    @Override
    public Collection<Thread> getBlockedThread() {
        //这里是只读的操作，但不是线程安全的，其他线程可以对他进行操作，所以用unmodifiableCollection()不能修改，修改则报错
        return Collections.unmodifiableCollection(blockedThreadCollection);
    }

    @Override
    public int getBlockedSize() {
        return blockedThreadCollection.size();
    }
}
